package tech

import (
	"slices"
	"ssp/set"
)

var FISHNAMES = []string{"X-Wing", "Swordfish", "Jellyfish", "Squirmbag"}

func (g *Grid) fishLines(lines []*House, cellCount int) (used bool, results []*Result) {
	for cand := 0; cand < 9; cand++ {
		verifiedLineNums := filterLineNums(lines, cand, cellCount)
		for _, lineNums := range combine(verifiedLineNums, cellCount) {
			ok, cellNums := verifyFishLine(lines, cand, lineNums, cellCount)
			if !ok {
				continue
			}
			if ok, rmCells := clearupFishLines(lines, cand, lineNums, cellNums); ok {
				used = true
				if OUTPUTRESULT {
					result := g.getFishResult(cand, rmCells, lineNums, cellNums, lines, cellCount)
					results = append(results, &result)
				}
				if CHANGE {
					removeCands(set.NewSet(cand), rmCells)
				}
				if !EXHAUSTIVE {
					return
				}
			}
		}
	}
	return
}

func (g *Grid) getFishResult(cand int, rmCells []*Cell, lineNums []int,
	cellNums []int, lines []*House, cellCount int) (result Result) {
	result.addRemoved(set.NewSet(cand), rmCells)
	xs := []*Cell{}
	for _, lineNum := range lineNums {
		for _, cellNum := range cellNums {
			xs = append(xs, lines[lineNum].cells[cellNum])
		}
	}
	group := result.getHelpCandsGroup()
	group.add(set.NewSet(cand), xs)
	lines1 := []*House{}
	for _, lineNum := range lineNums {
		lines1 = append(lines1, lines[lineNum])
	}
	lines2 := []*House{}
	if lines1[0].kind == 0 {
		for _, cellNum := range cellNums {
			lines2 = append(lines2, &g.cols[cellNum])
		}
	} else {
		for _, cellNum := range cellNums {
			lines2 = append(lines2, &g.rows[cellNum])
		}
	}
	result.addHelpHouses(lines1)
	result.addHelpHouses(lines2)
	result.setStrategyName(FISHNAMES[cellCount-2])

	return
}

func filterLineNums(lines []*House, cand int, cellCount int) []int {
	lineNums := make([]int, 0, 9)
	for lineNum, line := range lines {
		count := 0
		for _, cell := range line.cells {
			if cell.cands.Has(cand) {
				count++
			}
		}
		if count == 0 || count > cellCount {
			continue
		}
		lineNums = append(lineNums, lineNum)
	}
	return lineNums
}

func verifyFishLine(lines []*House, cand int, lineNums []int, cellCount int) (ok bool, cellNums []int) {
	cellNumSet := set.NewSet()
	for _, lineNum := range lineNums {
		for cellNum, cell := range lines[lineNum].cells {
			if cell.cands.Has(cand) {
				cellNumSet.Add(cellNum)
			}
		}
	}
	cellNums = cellNumSet.GetList()
	ok = len(cellNums) == cellCount
	return
}

func clearupFishLines(lines []*House, cand int, lineNums []int, cellNums []int) (
	used bool, rmCells []*Cell) {
	for lineNum, line := range lines {
		if slices.Contains(lineNums, lineNum) {
			continue
		}
		for _, cellNum := range cellNums {
			cell := line.cells[cellNum]
			if cell.solved() {
				continue
			}
			if cell.canRemoveCand(cand) {
				used = true
				rmCells = append(rmCells, cell)
			}
		}
	}
	return
}

func (g *Grid) fish(count int) (used bool, results []*Result) {
	if ok, rs := g.fishLines(g.lines[:9], count); ok {
		if OUTPUTRESULT {
			results = append(results, rs...)
		}
		used = true
	}
	if ok, rs := g.fishLines(g.lines[9:], count); ok {
		if OUTPUTRESULT {
			results = append(results, rs...)
		}
		used = true
	}
	return
}

func (s *Solve) XWing(g *Grid) (used bool, results []*Result) {
	return g.fish(2)
}
func (s *Solve) Swordfish(g *Grid) (used bool, results []*Result) {
	return g.fish(3)
}
func (s *Solve) Jellyfish(g *Grid) (used bool, results []*Result) {
	return g.fish(4)
}

/*
func (s *Solve) XWing(g *Grid) (used bool, results []*Result) {
	if ok, rs := g.fishLines(g.lines, 2); ok {
		if OUTPUTRESULT {
			results = append(results, rs...)
		}
		used = true
	}
	return
}

func (s *Solve) Swordfish(g *Grid) (used bool, results []*Result) {
	if ok, rs := g.fishLines(g.lines, 3); ok {
		if OUTPUTRESULT {
			results = append(results, rs...)
		}
		used = true
	}
	return
}

func (s *Solve) Jellyfish(g *Grid) (used bool, results []*Result) {
	return g.jellyfish()
}

func (g *Grid) jellyfish() (used bool, results []*Result) {
	if ok, rs := g.fishLines(g.lines[:9], 4); ok {
		if OUTPUTRESULT {
			results = append(results, rs...)
		}
		used = true
	}
	if ok, rs := g.fishLines(g.lines[9:], 4); ok {
		if OUTPUTRESULT {
			results = append(results, rs...)
		}
		used = true
	}
	return
}
*/
/*
func (s *Solve) Squirmbag(g *Grid) (used bool, results []*Result) {
	if ok, rs := g.fishLines(g.rows[:], 5); ok {
		results = append(results, rs...)
		used = true
	}
	if ok, rs := g.fishLines(g.cols[:], 5); ok {
		results = append(results, rs...)
		used = true
	}
	return
}
*/
